//
// Created by YueChuan on 2023/4/27.
//

#include <stdexcept>
#include <fstream>
#include "bytearray.h"
#include "endian.hpp"

namespace yuechuan {
    static Logger::ptr g_logger = YUECHUAN_LOG_NAME("root");

    ByteArray::Node::Node(size_t size)
            : ptr(new char[size]),
              next(nullptr),
              size(size) {

    }

    ByteArray::Node::Node()
            : ptr(nullptr),
              next(nullptr),
              size(0) {

    }

    ByteArray::Node::~Node() {
        if (ptr) {
            delete[] ptr;
        }
    }

    ByteArray::ByteArray(size_t base_size) :
            m_baseSize(base_size),
            m_capacity(base_size),
            m_position(0),
            m_size(0),
            m_endian(YUECHUAN_BIG_ENDIAN),
            m_root(new Node(base_size)),
            m_cur(m_root) {

    }

    ByteArray::~ByteArray() {
        Node *temp = m_root;
        while (temp) {
            m_cur = temp;
            temp = temp->next;
            delete m_cur;
        }
    }

    void ByteArray::writeFint8(int8_t value) {
        write(&value, sizeof(value));
    }


    void ByteArray::writeFuint8(uint8_t value) {
        write(&value, sizeof(value));
    }

    void ByteArray::writeFint16(int16_t value) {
        if (m_endian != YUECHUAN_BYTE_ORDER) {
            value = byteSwap(value);
        }
        write(&value, sizeof(value));

    }

    void ByteArray::writeFuint16(uint16_t value) {
        if (m_endian != YUECHUAN_BYTE_ORDER) {
            value = byteSwap(value);
        }
        write(&value, sizeof(value));
    }

    void ByteArray::writeFint32(int32_t value) {
        if (m_endian != YUECHUAN_BYTE_ORDER) {
            value = byteSwap(value);
        }
        write(&value, sizeof(value));
    }

    void ByteArray::writeFuint32(uint32_t value) {
        if (m_endian != YUECHUAN_BYTE_ORDER) {
            value = byteSwap(value);
        }
        write(&value, sizeof(value));
    }

    void ByteArray::writeFint64(int64_t value) {
        if (m_endian != YUECHUAN_BYTE_ORDER) {
            value = byteSwap(value);
        }
        write(&value, sizeof(value));
    }

    void ByteArray::writeFuint64(uint64_t value) {
        if (m_endian != YUECHUAN_BYTE_ORDER) {
            value = byteSwap(value);
        }
        write(&value, sizeof(value));
    }

    /**
     * @brief 将有符号的int转换为无符号的
     * @details 当一个int32_t类型的负数在进行压缩时，其二进制位大部分都是1
     *          很容易导致Varint压缩后得到最坏的效果就是占用5个字节
     *          将其转换为无符号的整形就可以避免这样的问题
     *          * Zigzag是将有符号数统一映射到无符号数的一种编码方案
     * @param v 要转换的数据
     * @return uint32_t
     */
    static uint32_t EncodeZigzag32(const int32_t &v) {
        if (v < 0) {
            return ((uint32_t) (-v)) * 2 - 1;
        } else {
            return v * 2;
        }
    }

    static int32_t DecodeZigzag32(const uint32_t &v) {
        return (v >> 1) ^ -(v & 1);
    }

    /**
     * @brief 将有符号的int64_t转换为无符号的数
     * @details 负数会导致Varint压缩出现最坏的情况，所以要将其转换为无符号的整形
     * @param v 要转换的整形
     * @return uint64_t
     */
    static uint64_t EncodeZigzag64(const int64_t &v) {
        if (v < 0) {
            return ((uint64_t) (-v) * 2 - 1);
        } else {
            return v * 2;
        }
    }

    static int64_t DecodeZigzag64(const uint64_t &v) {
        return (v >> 1) ^ -(v & 1);
    }

    void ByteArray::writeInt32(int32_t value) {
        writeUint32(EncodeZigzag32(value));
    }

    void ByteArray::writeUint32(uint32_t value) {
        uint8_t tmp[5];
        uint8_t i = 0;
        while (value >= 0x80) {
            // 从低到高取7位，在最高位加上最高有效位MSB，然后从高到低进行保存
            tmp[i++] = 0x80 | (value & 0x7F);
            value >>= 7;
        }
        tmp[i++] = value;
        write(tmp, i);
    }

    void ByteArray::writeInt64(int64_t value) {
        writeUint64(EncodeZigzag64(value));
    }

    void ByteArray::writeUint64(uint64_t value) {
        uint8_t tmp[10];
        uint8_t i = 0;
        while (value >= 0x80) {
            tmp[i++] = 0x80 | (value & 0x7F);
            value >>= 7;
        }
        tmp[i++] = value;
        write(tmp, i);
    }

    void ByteArray::writeFloat(float value) {
        uint32_t v;
        memcpy(&v, &value, sizeof(value));
        writeFuint32(v);
    }

    void ByteArray::writeDouble(double value) {
        uint64_t v;
        memcpy(&v, &value, sizeof(value));
        writeFuint64(v);
    }

    void ByteArray::writeStringF16(const std::string &value) {
        writeFuint16(value.size());
        write(value.c_str(), value.size());
    }

    void ByteArray::writeStringF32(const std::string &value) {
        writeFuint32(value.size());
        write(value.c_str(), value.size());
    }

    void ByteArray::writeStringF64(const std::string &value) {
        writeFuint64(value.size());
        write(value.c_str(), value.size());
    }

    void ByteArray::writeStringVint(const std::string &value) {
        writeFuint64(value.size());
        write(value.c_str(), value.size());
    }

    void ByteArray::writeStringWithoutLength(const std::string &value) {
        write(value.c_str(), value.size());
    }


    int8_t ByteArray::readFint8() {
        int8_t v;
        read(&v, sizeof(v));
        return v;

    }

    uint8_t ByteArray::readFuint8() {
        uint8_t v;
        read(&v, sizeof(v));
        return v;
    }

#define XX(type) \
    type v;      \
    read(&v, sizeof(v)); \
    if(m_endian == YUECHUAN_BYTE_ORDER){ \
        return v;    \
    }else{       \
        return byteSwap(v);  \
    }

    int16_t ByteArray::readFint16() {
        XX(int16_t);
    }

    uint16_t ByteArray::readFuint16() {
        XX(uint16_t);
    }

    int32_t ByteArray::readFint32() {
        XX(int32_t);
    }

    uint32_t ByteArray::readFuint32() {
        XX(uint32_t);
    }

    int64_t ByteArray::readFint64() {
        XX(int64_t);
    }

    uint64_t ByteArray::readFuint64() {
        XX(uint64_t);
    }

#undef XX

    int32_t ByteArray::readInt32() {
        return DecodeZigzag32(readUint32());
    }

    uint32_t ByteArray::readUint32() {
        uint32_t result = 0;
        for (int i = 0; i < 32; i += 7) {
            uint8_t b = readFuint8();
            if (b < 0x80) {
                result |= ((uint32_t) b) << i;
                break;
            } else {
                result |= (((uint32_t) (b & 0x7f)) << i);
            }
        }
        return result;
    }

    int64_t ByteArray::readInt64() {
        return DecodeZigzag64(readUint64());
    }

    uint64_t ByteArray::readUint64() {
        uint64_t result = 0;
        for (int i = 0; i < 32; i += 7) {
            uint8_t b = readFuint8();
            if (b < 0x80) {
                result |= ((uint64_t) b) << i;
                break;
            } else {
                result |= (((uint64_t) (b & 0x7f)) << i);
            }
        }
        return result;
    }

    float ByteArray::readFloat() {
        uint32_t v = readFuint32();
        float value;
        memcpy(&value, &v, sizeof(v));
        return value;
    }

    double ByteArray::readDouble() {
        uint64_t v = readFuint64();
        double value;
        memcpy(&value, &v, sizeof(v));
        return value;
    }

    std::string ByteArray::readStringF16() {
        uint16_t len = readFuint16();
        std::string buff;
        buff.resize(len);
        read(&buff[0], len);
        return buff;
    }

    std::string ByteArray::readStringF32() {
        uint32_t len = readFuint32();
        std::string buff;
        buff.resize(len);
        read(&buff[0], len);
        return buff;
    }

    std::string ByteArray::readStringF64() {
        uint64_t len = readFuint64();
        std::string buff;
        buff.resize(len);
        read(&buff[0], len);
        return buff;
    }

    std::string ByteArray::readStringVint() {
        uint64_t len = readUint64();
        std::string buff;
        buff.resize(len);
        read(&buff[0], len);
        return buff;
    }

    void ByteArray::clear() {
        m_position = m_size = 0;
        m_capacity = m_baseSize;
        Node *tmp = m_root->next;
        while (tmp) {
            m_cur = tmp;
            tmp = tmp->next;
            delete[] m_cur;
        }
        m_cur = m_root;
        m_root->next = nullptr;
    }

    void ByteArray::write(const void *buf, size_t size) {
        if (size == 0) {
            return;
        }
        addCapacity(size);
        /// 在当前节点的位置
        size_t npos = m_position % m_baseSize;
        /// 当前节点的容量
        size_t ncap = m_cur->size - npos;
        /// 偏移量
        size_t bpos = 0;
        while (size > 0) {
            if (ncap >= size) {
                memcpy(m_cur->ptr + npos, (const char *) buf + bpos, size);
                if (m_cur->size == (npos + size)) {
                    m_cur = m_cur->next;
                }
                m_position += size;
                bpos += size;
                size = 0;
            } else {
                memcpy(m_cur->ptr + npos, (const char *) buf + bpos, ncap);
                m_position += ncap;
                bpos += ncap;
                size -= ncap;
                m_cur = m_cur->next;
                ncap = m_cur->size;
                npos = 0;
            }
        }
        if (m_position > m_size) {
            m_size = m_position;
        }
    }

    void ByteArray::read(void *buf, size_t size) {
        if (size > getReadSize()) {
            throw std::out_of_range("not enough length");
        }
        size_t npos = m_position % m_baseSize;
        size_t ncap = m_cur->size - npos;
        size_t bpos = 0;
        while (size > 0) {
            if (ncap >= size) {
                memcpy((char *) buf + bpos, m_cur->ptr + npos, size);
                if (m_cur->size == (npos + size)) {
                    m_cur = m_cur->next;
                }
                m_position += size;
                bpos += size;
                size = 0;
            } else {
                memcpy((char *) buf + bpos, m_cur->ptr + npos, ncap);
                m_position += ncap;
                bpos += ncap;
                size -= ncap;
                m_cur = m_cur->next;
                ncap = m_cur->size;
                npos = 0;
            }
        }
    }

    void ByteArray::read(void *buf, size_t size, size_t position) const {
        if (size > (m_size - position)) {
            throw std::out_of_range("not enough length");
        }
        // 获取在当前节点中的内存偏移位置
        size_t npos = position % m_baseSize;
        // 获取当前节点中剩余的容量
        size_t ncap = m_cur->size - npos;
        // 已经读取的字节数
        size_t bpos = 0;
        // 游标节点，指向当前节点
        Node *cur = m_cur;
        while (size > 0) {
            if (ncap >= size) {
                // 当容量大小 大于 要读取的大小，直接从节点中将去读取到buf中
                memcpy((char *) buf + bpos, cur->ptr + npos, size);
                if (cur->size == (npos + size)) {
                    // 读取后发现当前节点的内存大小刚好被全部读取，则游标向下移动一个
                    cur = cur->next;
                }
                position += size;
                bpos += size;
                size = 0;
            } else {
                // 当容量大小 小于 要读取的大小，则先读取容量大小，然后游标向下移动
                memcpy((char *) buf + bpos, cur->ptr + npos, ncap);
                position += ncap;
                // 已经读取的大小加上容量大小
                bpos += ncap;
                // 需要读取的大小减去容量大小
                size -= ncap;
                // 游标向下移动一种
                cur = cur->next;
                // 容量更新为游标的大小
                ncap = cur->size;
                // 默认下一个节点从0位置开始偏移
                npos = 0;
            }
        }
    }

    void ByteArray::setPosition(size_t position) {
        // 设置偏移量如果大于容量，则抛出异常
        if (position > m_capacity) {
            throw std::out_of_range("setPosition out of range");
        }
        m_position = position;
        // 当前操作的位置大于数据的大小
        if (m_position > m_size) {
            m_size = m_position;
        }
        // 设置当前节点的位置，通过position和节点的大小进行比较，position
        m_cur = m_root;
        while (position > m_cur->size) {
            position -= m_cur->size;
            m_cur = m_cur->next;
        }
        if (position == m_cur->size) {
            m_cur = m_cur->next;
        }
    }

    bool ByteArray::writeToFile(const std::string &name) const {
        std::ofstream ofs;
        ofs.open(name, std::ios::trunc | std::ios::binary);
        if (!ofs) {
            YUECHUAN_LOG_ERROR(g_logger) << "writeToFile error name = "
                                         << name << " errno = " << errno << " str = " << strerror(errno);
            return false;
        }
        int64_t read_size = getReadSize();// 获取读取的字节数
        int64_t pos = m_position; // 当前位置
        Node *cur = m_cur; // 当前指针
        while (read_size > 0) {
            int diff = pos % m_baseSize; // 当前位置的偏移量
            // 如果要读取的size 大于 baseSize，则先读取baseSize在减去偏移量，否则直接读取read_size减去偏移量
            int64_t len = (read_size > (int64_t) m_baseSize ? m_baseSize : read_size) - diff;
            // 从偏移量的位置开始写入len个字节
            ofs.write(cur->ptr + diff, len);
            // 当下次循环时表明read_size>baseSize， 当前节点已经放不下了，所以要保存到下个节点中
            // 当不进入循环时，下面这条语句无效
            cur = cur->next;
            // 当前偏移位置要加上新写入的大小
            pos += len;
            // 要读取的大小要减去已经写入的大小，当为负时终止循环
            read_size -= len;
        }
        return true;
    }


    bool ByteArray::readFromFile(const std::string &name) {
        std::ifstream ifs;
        ifs.open(name, std::ios::binary);
        if (!ifs) {
            YUECHUAN_LOG_ERROR(g_logger) << "readFromFile error name = "
                                         << name << " errno " << errno << " " << strerror(errno);
            return false;
        }
        std::shared_ptr<char> buff(new char[m_baseSize], [](char *ptr) {
            delete[] ptr;
        });// 因为数组中使用的是char指针，所以在释放时要自定义析构函数，使用delete[]来释放空间，不然会内存泄漏
        while (!ifs.eof()) {
            ifs.read(buff.get(), m_baseSize);// 先读取baseSize个字节到buff中
            write(buff.get(), ifs.gcount());  // 再将读取到的字节数写入到buff中
        }
        return true;
    }

    void ByteArray::addCapacity(size_t size) {
        if (size == 0) {
            return;
        }
        size_t old_cap = getCapacity();
        if (old_cap >= size) {
            return;
        }
        size = size - old_cap;
        size_t count = ceil(1.0 * size / m_baseSize);//  需要增加的节点数
        Node *tmp = m_root;
        while (tmp->next) {
            tmp = tmp->next;
        }
        Node *first = nullptr; // 新增节点的第一个节点
        for (size_t i = 0; i < count; ++i) {
            tmp->next = new Node(m_baseSize);
            if (first == nullptr) {
                first = tmp->next;
            }
            tmp = tmp->next;
            m_capacity += m_baseSize;
        }
        if (old_cap == 0) {
            m_cur = first;          // 当原先的容量为0时，当前节点就应该为新增节点的第一个节点
        }
    }

    bool ByteArray::isLittleEndian() const {
        return m_endian == YUECHUAN_LITTLE_ENDIAN;
    }

    void ByteArray::setLittleEndian(bool val) {
        if (val) {
            m_endian = YUECHUAN_LITTLE_ENDIAN;
        } else {
            m_endian = YUECHUAN_BIG_ENDIAN;
        }
    }

    std::string ByteArray::toString() const {
        std::string str;
        str.resize(getReadSize());
        if (str.empty()) {
            return str;
        }
        read(&str[0], str.size(), m_position);
        return str;

    }

    std::string ByteArray::toHexString() const {
        std::string str = toString();
        std::stringstream ss;
        for (size_t i = 0; i < str.size(); ++i) {
            if (i > 0 && i % 32 == 0) {
                ss << std::endl;
            }
            ss << std::setw(2) << std::setfill('0')
               << std::hex << (int) (uint8_t) str[i] << " ";
        }
        return ss.str();
    }

    uint64_t ByteArray::getReadBuffers(std::vector<iovec> &buffers, uint64_t len) const {
        len = len > getReadSize() ? getReadSize() : len;
        if (len == 0) {
            return 0;

        }
        uint64_t size = len;
        size_t npos = m_position % m_baseSize;
        size_t ncap = m_cur->size - npos;
        struct iovec iov;
        Node *cur = m_cur;
        while (len > 0) {
            if (ncap >= len) {
                iov.iov_base = cur->ptr + npos;
                iov.iov_len = len;
                len = 0;
            } else {
                iov.iov_base = cur->ptr + npos;
                iov.iov_len = ncap;
                len -= ncap;
                cur = cur->next;
                ncap = cur->size;
                npos = 0;
            }
            buffers.emplace_back(iov);
        }
        return size;
    }

    uint64_t ByteArray::getReadBuffers(std::vector<iovec> &buffers, uint64_t len, uint64_t position) const {
        len = len > getReadSize() ? getReadSize() : len;
        if (len == 0) {
            return 0;
        }
        uint64_t size = len;

        size_t npos = position % m_baseSize;
        size_t count = position / m_baseSize;
        Node *cur = m_root;
        while (count > 0) {
            cur = cur->next;
            --count;
        }
        size_t ncap = cur->size - npos;
        struct iovec iov;
        while (len > 0) {
            if (ncap >= len) {
                iov.iov_base = cur->ptr + npos;
                iov.iov_len = ncap;
                len -= ncap;
                cur = cur->next;
                ncap = cur->size;
                npos = 0;
            }
            buffers.emplace_back(iov);
        }
        return size;
    }

    uint64_t ByteArray::getWriteBuffers(std::vector<iovec> &buffers, uint64_t len) {
        if (len == 0) {
            return 0;
        }
        addCapacity(len);
        uint64_t size = len;
        size_t npos = m_position % m_baseSize;
        size_t ncap = m_cur->size - npos;
        struct iovec iov;
        Node *cur = m_cur;
        while (len > 0) {
            if (ncap >= len) {
                iov.iov_base = cur->ptr + npos;
                iov.iov_len = len;
                len = 0;
            } else {
                iov.iov_base = cur->ptr + npos;
                iov.iov_len = ncap;

                len -= ncap;
                cur = cur->next;
                ncap = cur->size;
                npos = 0;
            }
            buffers.emplace_back(iov);
        }
        return size;

    }
}// namespace yuechuan